package sun.cn.common.cacheDto.proxy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import redis.clients.jedis.BinaryJedis;
import sun.cn.common.cacheDto.annotations.CacheColumn;
import sun.cn.common.cacheDto.constants.Constants;
import sun.cn.common.cacheDto.constants.MethodType;
import sun.cn.common.cacheDto.entity.Entity;
import sun.cn.common.cacheDto.utils.RedisUtils;
import sun.cn.common.utils.StringExtendUtils;

/**
 * 实现缓存代理
 * <p>
 * 利用redis实现热点数据缓存
 * </p>
 * 
 * @author sunlingao
 * @title 实现缓存代理
 * @date 2017年7月6日
 * @version
 */
public class CacheProxy extends ServiceProxy {

	private static final Logger LOG = LoggerFactory.getLogger(CacheProxy.class);
	
	private BinaryJedis jedis;

	private static final String KEY_FORMAT = "%s_%s";

	private static final String[] METHOD_ARR = { Constants.DELETE, Constants.GET, Constants.SAVE, Constants.SEARCH,
			Constants.UPDATE };

	public CacheProxy(BinaryJedis jedis){
        this.jedis = jedis;
    }

	@Override
	public String getProxyName() {
		return "cacheProxy";
	}

	@Override
	public Object executor(Object[] params, Method method, Object obj) throws Exception {
		MethodType type = getMthodType(method.getName());
		try {
			if (type == null) {
				LOG.warn("cache proxy methodtype is null and methodname is {}", method.getName());
				return method.invoke(obj, params);
			}
			switch (type) {
			case GET:
				return getInvoke(params, method, obj);
			case UPDATE:
				return updateInvoke(params, method, obj);
			case SEARCH:
				return method.invoke(obj, params);
			case DELETE:
				return deleteInvoke(params, method, obj);
			case SAVE:
				return saveInvoke(params, method, obj);
			default:
				return method.invoke(obj, params);
			}
		} catch (Exception e) {
			LOG.error("CacheProxy-executor method is {} ex is {}", method.getName(), e);
			throw e;
		}
	}

	private Object getInvoke(Object[] params, Method method, Object obj) throws Exception {
		Object entity = fromArgsGetEntity(params);
		if (entity == null) {
			LOG.warn("get查询请继承entity接口");
			return null;
		}
		List<Field> fields = getEntityCacheColumnKey(entity);
		Object cacheObj;
		if(CollectionUtils.isNotEmpty(fields)) {
			for (Field field : fields) {
				Object value = getUseAbleValue(field.get(entity));
				if (value != null) {
					cacheObj = RedisUtils.hGet(StringExtendUtils.getClassName(obj.getClass().getName()),
							String.format(KEY_FORMAT, field.getName(), value), jedis);
					if (cacheObj != null)
						return cacheObj;
				}
			}
		}
		Object invokeObj = method.invoke(obj, params);
		for (Field field : fields) {
			Object value = getUseAbleValue(field.get(entity));
			if (value != null&&invokeObj!=null) {
                RedisUtils.hSet(StringExtendUtils.getClassName(obj.getClass().getName()),
						String.format(KEY_FORMAT, field.getName(), value), invokeObj,jedis);
			}
		}
		return invokeObj;
	}

	private Object updateInvoke(Object[] params, Method method, Object obj) throws Exception {
		Object invokeObj = method.invoke(obj, params);
        RedisUtils.delKey(StringExtendUtils.getClassName(obj.getClass().getName()),jedis);
		return invokeObj;
	}

	private Object saveInvoke(Object[] params, Method method, Object obj) throws Exception {
		Object entity = fromArgsGetEntity(params);
		Object invokeObj = method.invoke(obj, params);
		List<Field> fields = getEntityCacheColumnKey(entity);
		for (Field field : fields) {
			Object value = getUseAbleValue(field.get(entity));
			if (value != null && entity!=null) {
                RedisUtils.hSet(StringExtendUtils.getClassName(obj.getClass().getName()),
						String.format(KEY_FORMAT, field.getName(), value), entity,jedis);
			}
		}
		return invokeObj;
	}

	private Object deleteInvoke(Object[] params, Method method, Object obj) throws Exception {
		Object invokeObj = method.invoke(obj, params);
        RedisUtils.delKey(StringExtendUtils.getClassName(obj.getClass().getName()),jedis);
		return invokeObj;
	}

	private MethodType getMthodType(String methodName) {
		if (StringExtendUtils.isNotBlank(methodName)) {
			for (String s : METHOD_ARR) {
				if (methodName.startsWith(s)) {
					return MethodType.findById(s);
				}
			}
		}
		return null;
	}

	/**
	 * 从入参中获取entity实体
	 * 
	 * @author sunlingao@zbj.com
	 * @date 2017年7月8日
	 * @version
	 * @param params
	 * @return
	 */
	private Object fromArgsGetEntity(Object... params) {
		for (Object param : params) {
			if (Entity.class.isAssignableFrom(param.getClass())) {
				return param;
			}
		}
		return null;
	}

	/**
	 * 从实体中获取主键值
	 * 
	 * @author sunlingao
	 * @date 2017年7月8日
	 * @version
	 * @param param
	 * @return
	 */
	private List<Field> getEntityCacheColumnKey(Object param) {
		if (param == null)
			return null;
		List<Field> list = Lists.newArrayList();
		Field[] fields = param.getClass().getDeclaredFields();
		for (Field field : fields) {
			CacheColumn idan = field.getAnnotation(CacheColumn.class);
			if (idan != null) {
				field.setAccessible(true);
				list.add(field);
			}
		}
		return list;
	}

	private Object getUseAbleValue(Object value) {
		if (value != null) {
			if (value instanceof String) {
				if (StringExtendUtils.isNotBlank(String.valueOf(value))) {
					return value;
				}
			} else {
				return NumberUtils.toDouble(String.valueOf(value), 0D) == 0D ? null : value;
			}
		}
		return null;
	}

}
